package xutil

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"encoding/gob"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"net/url"
	"reflect"
	"strings"

	"github.com/techoner/gophp/serialize"
)

// to md5
func Md5(v any) (string, error) {
	json_bytes := []byte{}
	var e error

	switch v.(type) {
	case string:
		json_bytes = []byte(v.(string))
	default:
		json_bytes, e = json.Marshal(v)
		if e != nil {
			return "", e
		}
	}

	md5sum := md5.Sum(json_bytes)

	return hex.EncodeToString(md5sum[:]), nil
}

// base64
func Base64Encode(v any) (string, error) {
	json_bytes := fmt.Sprintf("%v", v)
	return base64.StdEncoding.EncodeToString([]byte(json_bytes)), nil
}
func Base64Decode(base64str string) (string, error) {
	str, e := base64.StdEncoding.DecodeString(base64str)
	if e != nil {
		return "", e
	}
	return string(str[:]), nil
}

// gob
func GobEncode(value any) ([]byte, error) {
	buf := &bytes.Buffer{}
	e := gob.NewEncoder(buf).Encode(value)
	if e != nil {
		return nil, e
	}

	return buf.Bytes(), nil
}
func GobDecode(buffer []byte, ptrValue any) error {
	buf := &bytes.Buffer{}
	buf.Write(buffer)

	return gob.NewDecoder(buf).Decode(ptrValue)
}

func JSON(v any) (string, error) {
	_bytes, e := json.Marshal(v)
	if e != nil {
		return "{}", e
	}

	return string(_bytes[:]), nil
}

func UNJSON(str string, v any) error {
	decoder := json.NewDecoder(strings.NewReader(str))
	decoder.UseNumber()

	e := decoder.Decode(&v)
	if e != nil {
		return e
	}

	return nil
}

func IS_JSON(str string) bool {
	e := UNJSON(str, map[string]any{})
	if e != nil {
		return false
	}

	return true
}

// serialize
func Serialize(v any) ([]byte, error) {
	return serialize.Marshal(v)
}
func Unserialize(data []byte) (any, error) {
	return serialize.UnMarshal(data)
}

//map
func Struct2Map(obj any) map[string]any {
	if reflect.Map == reflect.TypeOf(obj).Kind() {
		return obj.(map[string]any)
	}

	if reflect.Struct != reflect.TypeOf(obj).Kind() {
		return nil
	}

	data := map[string]any{}

	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	for i := 0; i < t.NumField(); i++ {
		//替换key
		Name := func(Name string) string {
			if strings.Contains(Name, "omitempty") {
				tags := strings.Split(Name, ",")
				new_tags := []string{}
				for _, v := range tags {
					if v != "omitempty" {
						new_tags = append(new_tags, v)
					}
				}

				Name = strings.Join(new_tags, ",")
			}
			return Name
		}(t.Field(i).Tag.Get("json"))

		if "" == Name {
			Name = t.Field(i).Name
		}

		kind := v.Field(i).Kind()
		if reflect.Struct == kind {
			data[Name] = Struct2Map(v.Field(i).Interface())
		} else {
			if v.Field(i).CanInterface() {
				data[Name] = v.Field(i).Interface()
			}
		}
	}

	return data
}

//url.Values
func Map2UrlValues(mp map[string]any) url.Values {
	values := url.Values{}

	for k, v := range mp {
		values.Set(k, fmt.Sprintf("%v", v))
	}

	return values
}
